package upload

import (
	"errors"
	"fmt"
	"mime/multipart"
	"path/filepath"
	"strings"
	"x-admin/backend/pkg/storage"

	"gorm.io/gorm"
)

// Service 文件上传业务逻辑层接口
type Service interface {
	Upload(file *multipart.FileHeader, req *UploadRequest, userID *uint) (*UploadResponse, error)
	Delete(req *DeleteRequest, userID *uint) error
	BatchDelete(req *BatchDeleteRequest, userID *uint) error
	GetByID(id uint) (*FileInfo, error)
	List(req *QueryRequest) ([]*FileInfo, int64, error)
	GetUploadConfig() *UploadConfig
}

// service 文件上传业务逻辑层实现
type service struct {
	repo           Repository
	storageManager *storage.Manager
	config         *UploadConfig
}

// NewService 创建文件上传业务逻辑层
func NewService(repo Repository, storageManager *storage.Manager) Service {
	return &service{
		repo:           repo,
		storageManager: storageManager,
		config: &UploadConfig{
			MaxFileSize:   10 * 1024 * 1024, // 10MB
			AllowedTypes:  []string{"image", "document", "video", "audio"},
			AllowedExts:   []string{".jpg", ".jpeg", ".png", ".gif", ".pdf", ".doc", ".docx", ".xls", ".xlsx", ".mp4", ".mp3"},
			StorageDriver: "local",
			UploadPath:    "uploads",
		},
	}
}

// Upload 上传文件
func (s *service) Upload(file *multipart.FileHeader, req *UploadRequest, userID *uint) (*UploadResponse, error) {
	// 验证文件
	if err := s.validateFile(file); err != nil {
		return nil, err
	}

	// 检查文件是否已存在（通过哈希值）
	fileContent, err := s.getFileContent(file)
	if err != nil {
		return nil, fmt.Errorf("读取文件内容失败: %w", err)
	}

	hash := storage.GenerateSecureFilenameWithSHA256(file.Filename, fileContent)

	// 检查是否已存在相同文件
	existingFile, err := s.repo.GetByHash(hash)
	if err == nil && existingFile != nil {
		// 文件已存在，返回现有文件信息
		return &UploadResponse{
			ID:           existingFile.ID,
			OriginalName: existingFile.OriginalName,
			FileName:     existingFile.FileName,
			FileURL:      existingFile.FileURL,
			FileSize:     existingFile.FileSize,
			FileType:     existingFile.FileType,
			MimeType:     existingFile.MimeType,
			Extension:    existingFile.Extension,
			Hash:         existingFile.Hash,
		}, nil
	}

	// 上传文件到存储
	uploadResult, err := s.storageManager.UploadFile(file, req.Category)
	if err != nil {
		return nil, fmt.Errorf("文件上传失败: %w", err)
	}

	// 创建文件记录
	fileRecord := &File{
		OriginalName: file.Filename,
		FileName:     uploadResult.StoredName,
		FilePath:     uploadResult.Path,
		FileURL:      uploadResult.URL,
		FileSize:     file.Size,
		FileType:     s.getFileType(file.Filename),
		MimeType:     s.getMimeType(file),
		Extension:    strings.ToLower(filepath.Ext(file.Filename)),
		Hash:         hash,
		StorageType:  s.config.StorageDriver,
		UserID:       userID,
		Status:       1,
	}

	if err := s.repo.Create(fileRecord); err != nil {
		// 如果数据库保存失败，尝试删除已上传的文件
		s.storageManager.DeleteFile(uploadResult.Path)
		return nil, fmt.Errorf("保存文件记录失败: %w", err)
	}

	return &UploadResponse{
		ID:           fileRecord.ID,
		OriginalName: fileRecord.OriginalName,
		FileName:     fileRecord.FileName,
		FileURL:      fileRecord.FileURL,
		FileSize:     fileRecord.FileSize,
		FileType:     fileRecord.FileType,
		MimeType:     fileRecord.MimeType,
		Extension:    fileRecord.Extension,
		Hash:         fileRecord.Hash,
	}, nil
}

// Delete 删除文件
func (s *service) Delete(req *DeleteRequest, userID *uint) error {
	var file *File
	var err error

	// 根据不同参数查找文件
	if req.FileID != nil {
		file, err = s.repo.GetByID(*req.FileID)
	} else if req.FileName != nil {
		file, err = s.repo.GetByFileName(*req.FileName)
	} else if req.FileURL != nil {
		// 从URL中提取文件名
		fileName := filepath.Base(*req.FileURL)
		file, err = s.repo.GetByFileName(fileName)
	} else {
		return errors.New("必须提供文件ID、文件名或文件URL")
	}

	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return errors.New("文件不存在")
		}
		return err
	}

	// 检查权限（如果提供了userID）
	if userID != nil && file.UserID != nil && *file.UserID != *userID {
		return errors.New("无权限删除此文件")
	}

	// 删除物理文件
	if err := s.storageManager.DeleteFile(file.FilePath); err != nil {
		// 记录日志，但不阻止数据库删除
		fmt.Printf("删除物理文件失败: %v\n", err)
	}

	// 删除数据库记录
	return s.repo.Delete(file.ID)
}

// BatchDelete 批量删除文件
func (s *service) BatchDelete(req *BatchDeleteRequest, userID *uint) error {
	for _, fileID := range req.FileIDs {
		deleteReq := &DeleteRequest{FileID: &fileID}
		if err := s.Delete(deleteReq, userID); err != nil {
			return fmt.Errorf("删除文件ID %d 失败: %w", fileID, err)
		}
	}
	return nil
}

// GetByID 根据ID获取文件信息
func (s *service) GetByID(id uint) (*FileInfo, error) {
	file, err := s.repo.GetByID(id)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.New("文件不存在")
		}
		return nil, err
	}

	return &FileInfo{
		ID:           file.ID,
		OriginalName: file.OriginalName,
		FileName:     file.FileName,
		FilePath:     file.FilePath,
		FileURL:      file.FileURL,
		FileSize:     file.FileSize,
		FileType:     file.FileType,
		MimeType:     file.MimeType,
		Extension:    file.Extension,
		Hash:         file.Hash,
		StorageType:  file.StorageType,
		UserID:       file.UserID,
		Status:       file.Status,
		CreatedAt:    file.CreatedAt,
		UpdatedAt:    file.UpdatedAt,
	}, nil
}

// List 获取文件列表
func (s *service) List(req *QueryRequest) ([]*FileInfo, int64, error) {
	// 设置默认分页参数
	if req.Page <= 0 {
		req.Page = 1
	}
	if req.PageSize <= 0 {
		req.PageSize = 10
	}
	if req.PageSize > 100 {
		req.PageSize = 100
	}

	return s.repo.List(req)
}

// GetUploadConfig 获取上传配置
func (s *service) GetUploadConfig() *UploadConfig {
	return s.config
}

// validateFile 验证文件
func (s *service) validateFile(file *multipart.FileHeader) error {
	// 检查文件大小
	if file.Size > s.config.MaxFileSize {
		return fmt.Errorf("文件大小超过限制，最大允许 %d 字节", s.config.MaxFileSize)
	}

	// 检查文件扩展名
	ext := strings.ToLower(filepath.Ext(file.Filename))
	if !s.isAllowedExtension(ext) {
		return fmt.Errorf("不支持的文件类型: %s", ext)
	}

	return nil
}

// isAllowedExtension 检查是否为允许的扩展名
func (s *service) isAllowedExtension(ext string) bool {
	for _, allowedExt := range s.config.AllowedExts {
		if ext == allowedExt {
			return true
		}
	}
	return false
}

// getFileType 根据文件名获取文件类型
func (s *service) getFileType(filename string) string {
	ext := strings.ToLower(filepath.Ext(filename))
	switch ext {
	case ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp":
		return "image"
	case ".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".txt":
		return "document"
	case ".mp4", ".avi", ".mov", ".wmv", ".flv", ".mkv":
		return "video"
	case ".mp3", ".wav", ".flac", ".aac", ".ogg":
		return "audio"
	default:
		return "other"
	}
}

// getMimeType 获取MIME类型
func (s *service) getMimeType(file *multipart.FileHeader) string {
	if file.Header != nil {
		return file.Header.Get("Content-Type")
	}
	return "application/octet-stream"
}

// getFileContent 获取文件内容
func (s *service) getFileContent(file *multipart.FileHeader) ([]byte, error) {
	src, err := file.Open()
	if err != nil {
		return nil, err
	}
	defer src.Close()

	content := make([]byte, file.Size)
	_, err = src.Read(content)
	return content, err
}
